home *** CD-ROM | disk | FTP | other *** search
/ Aminet 45 / Aminet 45 (2001)(GTI - Schatztruhe)[!][Oct 2001].iso / Aminet / gfx / x11 / x3270_3_2_16.lha / amiga_src / printer.c < prev    next >
C/C++ Source or Header  |  2001-06-23  |  11KB  |  473 lines

  1. /*
  2.  * Copyright 2000 by Paul Mattes.
  3.  *  Permission to use, copy, modify, and distribute this software and its
  4.  *  documentation for any purpose and without fee is hereby granted,
  5.  *  provided that the above copyright notice appear in all copies and that
  6.  *  both that copyright notice and this permission notice appear in
  7.  *  supporting documentation.
  8.  */
  9.  
  10. /*
  11.  *    printer.c
  12.  *        Printer session support
  13.  */
  14.  
  15. #include "globals.h"
  16.  
  17. #if (defined(C3270) || defined(X3270_DISPLAY)) && defined(X3270_PRINTER) /*[*/
  18. #if defined(X3270_DISPLAY) /*[*/
  19. #include <X11/StringDefs.h>
  20. #include <X11/Xaw/Dialog.h>
  21. #endif /*]*/
  22. #include <errno.h>
  23. #include <signal.h>
  24. #include <time.h>
  25. #include <stdarg.h>
  26. #include <fcntl.h>
  27. #include "3270ds.h"
  28. #include "appres.h"
  29. #include "objects.h"
  30. #include "resources.h"
  31. #include "ctlr.h"
  32.  
  33. #include "ctlrc.h"
  34. #include "hostc.h"
  35. #include "menubarc.h"
  36. #include "popupsc.h"
  37. #include "printerc.h"
  38. #include "printc.h"
  39. #include "savec.h"
  40. #if defined(C3270) /*[*/
  41. #include "screenc.h"
  42. #endif /*]*/
  43. #include "tablesc.h"
  44. #include "telnetc.h"
  45. #include "trace_dsc.h"
  46. #include "utilc.h"
  47.  
  48. #define PRINTER_BUF    1024
  49.  
  50. /* Statics */
  51. static int      printer_pid = -1;
  52. #if defined(X3270_DISPLAY) /*[*/
  53. static Widget    lu_shell = (Widget)NULL;
  54. #endif /*]*/
  55. static struct pr3o {
  56.     int fd;            /* file descriptor */
  57.     unsigned long input_id;    /* input ID */
  58.     unsigned long timeout_id; /* timeout ID */
  59.     int count;        /* input count */
  60.     char buf[PRINTER_BUF];    /* input buffer */
  61. } printer_stdout = { -1, 0L, 0L, 0 },
  62.   printer_stderr = { -1, 0L, 0L, 0 };
  63.  
  64. static void    printer_output(void);
  65. static void    printer_error(void);
  66. static void    printer_otimeout(void);
  67. static void    printer_etimeout(void);
  68. static void    printer_dump(struct pr3o *p, Boolean is_err, Boolean is_dead);
  69. static void    printer_host_connect(Boolean connected unused);
  70.  
  71. /* Globals */
  72.  
  73. /*
  74.  * Printer Start-up function
  75.  * If 'lu' is non-NULL, then use the specific-LU form.
  76.  * If not, use the assoc form.
  77.  */
  78. void
  79. printer_start(const char *lu)
  80. {
  81.     const char *cmdlineName;
  82.     const char *cmdline;
  83.     const char *cmd;
  84.     int cmd_len = 0;
  85.     const char *s;
  86.     char *cmd_text;
  87.     char c;
  88.     int stdout_pipe[2];
  89.     int stderr_pipe[2];
  90.     static Boolean registered = False;
  91.  
  92. #if defined(X3270_DISPLAY) /*[*/
  93.     /* Make sure the popups are initted. */
  94.     printer_popup_init();
  95. #endif /*]*/
  96.  
  97.     /* Register interest in host connects and mode changes. */
  98.     if (!registered) {
  99.         register_schange(ST_CONNECT, printer_host_connect);
  100.         register_schange(ST_3270_MODE, printer_host_connect);
  101.         registered = True;
  102.     }
  103.  
  104.     /* Can't start two. */
  105.     if (printer_pid != -1) {
  106.         popup_an_error("printer is already running");
  107.         return;
  108.     }
  109.  
  110.     /* Gotta be in 3270 mode. */
  111.     if (!IN_3270) {
  112.         popup_an_error("Not in 3270 mode");
  113.         return;
  114.     }
  115.  
  116.     /* Select the command line to use. */
  117.     if (lu == CN) {
  118.         /* Associate with the current session. */
  119.  
  120.         /* Gotta be in TN3270E mode. */
  121.         if (!IN_TN3270E) {
  122.             popup_an_error("Not in TN3270E mode");
  123.             return;
  124.         }
  125.  
  126.         /* Gotta be connected to an LU. */
  127.         if (connected_lu == CN) {
  128.             popup_an_error("Not connected to a specific LU");
  129.             return;
  130.         }
  131.         lu = connected_lu;
  132.         cmdlineName = ResAssocCommand;
  133.     } else {
  134.         /* Specific LU passed in. */
  135.         cmdlineName = ResLuCommandLine;
  136.     }
  137.  
  138.     /* Fetch the command line and command resources. */
  139.     cmdline = get_resource(cmdlineName);
  140.     if (cmdline == CN) {
  141.         popup_an_error("%s resource not defined", cmdlineName);
  142.         return;
  143.     }
  144.     cmd = get_resource(ResPrinterCommand);
  145.     if (cmd == CN) {
  146.         popup_an_error("printer.command resource not defined");
  147.         return;
  148.     }
  149.  
  150.     /* Construct the command line. */
  151.  
  152.     /* Figure out how long it will be. */
  153.     cmd_len = strlen(cmdline) + 1;
  154.     s = cmdline;
  155.     while ((s = strstr(s, "%L%")) != CN) {
  156.         cmd_len += strlen(lu) - 3;
  157.         s += 3;
  158.     }
  159.     s = cmdline;
  160.     while ((s = strstr(s, "%H%")) != CN) {
  161.         cmd_len += strlen(hostname) - 3;
  162.         s += 3;
  163.     }
  164.     s = cmdline;
  165.     while ((s = strstr(s, "%C%")) != CN) {
  166.         cmd_len += strlen(cmd) - 3;
  167.         s += 3;
  168.     }
  169.  
  170.     /* Allocate a string buffer and substitute into it. */
  171.     cmd_text = Malloc(cmd_len);
  172.     cmd_text[0] = '\0';
  173.     for (s = cmdline; (c = *s) != '\0'; s++) {
  174.         char buf1[2];
  175.  
  176.         if (c == '%') {
  177.             if (!strncmp(s+1, "L%", 2)) {
  178.                 (void) strcat(cmd_text, lu);
  179.                 s += 2;
  180.                 continue;
  181.             } else if (!strncmp(s+1, "H%", 2)) {
  182.                 (void) strcat(cmd_text, hostname);
  183.                 s += 2;
  184.                 continue;
  185.             } else if (!strncmp(s+1, "C%", 2)) {
  186.                 (void) strcat(cmd_text, cmd);
  187.                 s += 2;
  188.                 continue;
  189.             }
  190.         }
  191.         buf1[0] = c;
  192.         buf1[1] = '\0';
  193.         (void) strcat(cmd_text, buf1);
  194.     }
  195.     trace_event("Printer command line: %s\n", cmd_text);
  196.  
  197. #ifndef AMIGA
  198.     /* Make pipes for printer's stdout and stderr. */
  199.     if (pipe(stdout_pipe) < 0) {
  200.         popup_an_errno(errno, "pipe() failed");
  201.         Free(cmd_text);
  202.         return;
  203.     }
  204.     (void) fcntl(stdout_pipe[0], F_SETFD, 1);
  205.     if (pipe(stderr_pipe) < 0) {
  206.         popup_an_errno(errno, "pipe() failed");
  207.         (void) close(stdout_pipe[0]);
  208.         (void) close(stdout_pipe[1]);
  209.         Free(cmd_text);
  210.         return;
  211.     }
  212.     (void) fcntl(stderr_pipe[0], F_SETFD, 1);
  213.  
  214.     /* Fork and exec the printer session. */
  215.     switch (printer_pid = fork()) {
  216.         case 0:    /* child process */
  217.         (void) dup2(stdout_pipe[1], 1);
  218.         (void) close(stdout_pipe[1]);
  219.         (void) dup2(stderr_pipe[1], 2);
  220.         (void) close(stderr_pipe[1]);
  221.         if (setsid() < 0) {
  222.             perror("setsid");
  223.             _exit(1);
  224.         }
  225.         (void) execlp("/bin/sh", "sh", "-c", cmd_text, CN);
  226.         (void) perror("exec(printer)");
  227.         _exit(1);
  228.         default:    /* parent process */
  229.         (void) close(stdout_pipe[1]);
  230.         printer_stdout.fd = stdout_pipe[0];
  231.         (void) close(stderr_pipe[1]);
  232.         printer_stderr.fd = stderr_pipe[0];
  233.         printer_stdout.input_id = AddInput(printer_stdout.fd,
  234.             printer_output);
  235.         printer_stderr.input_id = AddInput(printer_stderr.fd,
  236.             printer_error);
  237.         ++children;
  238.         break;
  239.         case -1:    /* error */
  240.         popup_an_errno(errno, "fork()");
  241.         (void) close(stdout_pipe[0]);
  242.         (void) close(stdout_pipe[1]);
  243.         (void) close(stderr_pipe[0]);
  244.         (void) close(stderr_pipe[1]);
  245.         break;
  246.     }
  247. #endif
  248.  
  249.     Free(cmd_text);
  250.  
  251.     /* Tell everyone else. */
  252.     st_changed(ST_PRINTER, True);
  253. }
  254.  
  255. /* There's data from the printer session. */
  256. static void
  257. printer_data(struct pr3o *p, Boolean is_err)
  258. {
  259.     int space;
  260.     int nr;
  261.     static char exitmsg[] = "Printer session exited";
  262.  
  263.     /* Read whatever there is. */
  264.     space = PRINTER_BUF - p->count - 1;
  265.     nr = read(p->fd, p->buf + p->count, space);
  266.  
  267.     /* Handle read errors and end-of-file. */
  268.     if (nr < 0) {
  269.         popup_an_errno(errno, "printer session pipe input");
  270.         printer_stop();
  271.         return;
  272.     }
  273.     if (nr == 0) {
  274.         if (printer_stderr.timeout_id != 0L) {
  275.             /*
  276.              * Append a termination error message to whatever the
  277.              * printer process said, and pop it up.
  278.              */
  279.             p = &printer_stderr;
  280.             space = PRINTER_BUF - p->count - 1;
  281.             if (p->count && *(p->buf + p->count - 1) != '\n') {
  282.                 *(p->buf + p->count) = '\n';
  283.                 p->count++;
  284.                 space--;
  285.             }
  286.             (void) strncpy(p->buf + p->count, exitmsg, space);
  287.             p->count += strlen(exitmsg);
  288.             if (p->count >= PRINTER_BUF)
  289.                 p->count = PRINTER_BUF - 1;
  290.             printer_dump(p, True, True);
  291.         } else {
  292.             popup_an_error(exitmsg);
  293.         }
  294.         printer_stop();
  295.         return;
  296.     }
  297.  
  298.     /* Add it to the buffer, and add a NULL. */
  299.     p->count += nr;
  300.     p->buf[p->count] = '\0';
  301.  
  302.     /*
  303.      * If there's no more room in the buffer, dump it now.  Otherwise,
  304.      * give it a second to generate more output.
  305.      */
  306.     if (p->count >= PRINTER_BUF - 1) {
  307.         printer_dump(p, is_err, False);
  308.     } else if (p->timeout_id == 0L) {
  309.         p->timeout_id = AddTimeOut(1000,
  310.             is_err? printer_etimeout: printer_otimeout);
  311.     }
  312. }
  313.  
  314. /* The printer process has some output for us. */
  315. static void
  316. printer_output(void)
  317. {
  318.     printer_data(&printer_stdout, False);
  319. }
  320.  
  321. /* The printer process has some error output for us. */
  322. static void
  323. printer_error(void)
  324. {
  325.     printer_data(&printer_stderr, True);
  326. }
  327.  
  328. /* Timeout from printer output or error output. */
  329. static void
  330. printer_timeout(struct pr3o *p, Boolean is_err)
  331. {
  332.     /* Forget the timeout ID. */
  333.     p->timeout_id = 0L;
  334.  
  335.     /* Dump the output. */
  336.     printer_dump(p, is_err, False);
  337. }
  338.  
  339. /* Timeout from printer output. */
  340. static void
  341. printer_otimeout(void)
  342. {
  343.     printer_timeout(&printer_stdout, False);
  344. }
  345.  
  346. /* Timeout from printer error output. */
  347. static void
  348. printer_etimeout(void)
  349. {
  350.     printer_timeout(&printer_stderr, True);
  351. }
  352.  
  353. /* Dump pending printer process output. */
  354. static void
  355. printer_dump(struct pr3o *p, Boolean is_err, Boolean is_dead)
  356. {
  357.     if (p->count) {
  358.         /*
  359.          * Strip any trailing newline, and make sure the buffer is
  360.          * NULL terminated.
  361.          */
  362.         if (p->buf[p->count - 1] == '\n')
  363.             p->buf[--(p->count)] = '\0';
  364.         else if (p->buf[p->count])
  365.             p->buf[p->count] = '\0';
  366.  
  367.         /* Dump it and clear the buffer. */
  368. #if defined(X3270_DISPLAY) /*[*/
  369.         popup_printer_output(is_err, is_dead? NULL: printer_stop,
  370.             "%s", p->buf);
  371. #else /*][*/
  372.         action_output("%s", p->buf);
  373. #endif
  374.         p->count = 0;
  375.     }
  376. }
  377.  
  378. /* Close the printer session. */
  379. void
  380. printer_stop(void)
  381. {
  382.     /* Remove inputs. */
  383.     if (printer_stdout.input_id) {
  384.         RemoveInput(printer_stdout.input_id);
  385.         printer_stdout.input_id = 0L;
  386.     }
  387.     if (printer_stderr.input_id) {
  388.         RemoveInput(printer_stderr.input_id);
  389.         printer_stderr.input_id = 0L;
  390.     }
  391.  
  392.     /* Cancel timeouts. */
  393.     if (printer_stdout.timeout_id) {
  394.         RemoveTimeOut(printer_stdout.timeout_id);
  395.         printer_stdout.timeout_id = 0L;
  396.     }
  397.     if (printer_stderr.timeout_id) {
  398.         RemoveTimeOut(printer_stderr.timeout_id);
  399.         printer_stderr.timeout_id = 0L;
  400.     }
  401.  
  402.     /* Clear buffers. */
  403.     printer_stdout.count = 0;
  404.     printer_stderr.count = 0;
  405.  
  406.     /* Kill the process. */
  407. #ifndef AMIGA
  408.     if (printer_pid != -1) {
  409.         (void) killpg(printer_pid, SIGTERM);
  410.         printer_pid = -1;
  411.     }
  412. #endif
  413.  
  414.     /* Tell everyone else. */
  415. #ifndef AMIGA
  416.     st_changed(ST_PRINTER, False);
  417. #endif
  418. }
  419.  
  420. #if defined(X3270_DISPLAY) /*[*/
  421. /* Callback for "OK" button on printer specific-LU popup */
  422. static void
  423. lu_callback(Widget w, XtPointer client_data, XtPointer call_data unused)
  424. {
  425.     char *lu;
  426.  
  427.     if (w) {
  428.         lu = XawDialogGetValueString((Widget)client_data);
  429.         if (lu == CN || *lu == '\0') {
  430.             popup_an_error("Must supply an LU");
  431.             return;
  432.         } else
  433.             XtPopdown(lu_shell);
  434.     } else
  435.         lu = (char *)client_data;
  436.     printer_start(lu);
  437. }
  438. #endif /*]*/
  439.  
  440. /* Host connect/disconnect/3270-mode event. */
  441. static void
  442. printer_host_connect(Boolean connected unused)
  443. {
  444.     /*
  445.      * If we're no longer in 3270 mode, then we can no longer have a
  446.      * printer session.  This may cause some fireworks if there is a
  447.      * print job pending when we do this, so some sort of awful timeout
  448.      * may be needed.
  449.      */
  450.     if (!IN_3270)
  451.         printer_stop();
  452. }
  453.  
  454. #if defined(X3270_DISPLAY) /*[*/
  455. /* Pop up the LU dialog box. */
  456. void
  457. printer_lu_dialog(void)
  458. {
  459.     if (lu_shell == NULL)
  460.         lu_shell = create_form_popup("printerLu",
  461.             lu_callback, (XtCallbackProc)NULL, FORM_NO_WHITE);
  462.     popup_popup(lu_shell, XtGrabExclusive);
  463. }
  464. #endif /*]*/
  465.  
  466. Boolean
  467. printer_running(void)
  468. {
  469.     return printer_pid != -1;
  470. }
  471.  
  472. #endif /*]*/
  473.